//
// Created by huxin on 2020/11/3.
//

#include "MJPGSender.h"

#include "Http/HttpRequest.h"
#include "Http/HttpResponse.h"

#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>

void MJPGSender::init(const std::string &listenAddr, int port, int timeout, int quality) {
    m_iPort = port;
    m_iTimeout = timeout;
    m_iQuality = quality;
    m_strListenAddr = listenAddr;
}

bool MJPGSender::release() {
    return m_listener.GetServer() && m_listener.GetServer()->Stop();
}

void MJPGSender::pubTestFrame() {
    cv::Mat tailFrame(cv::Size(std::abs(random()) * 10 % 1000 + 10, std::abs(random()) * 10 % 1000 + 10), CV_8UC3);
    publish(TEST_CHANNEL, tailFrame);
}

bool MJPGSender::open() {
    if (m_iPort < 0)
        return false;

    if (m_iQuality < 0)
        return false;

    if (m_iTimeout < 0)
        return false;

    bool bRes = m_listener.GetServer()->Start(m_strListenAddr.c_str(), m_iPort);
    return bRes;
}

bool MJPGSender::isOpened() {
    return m_listener.GetServer() && m_listener.GetServer()->HasStarted();
}

bool MJPGSender::publish(const std::string &channel, const cv::Mat &frame) {
    if (!m_listener.GetServer())
        return false;

    if (!m_listener.GetServer()->HasStarted())
        return false;

    auto mems = superType::channelMems(channel);

    if (mems.empty())
        return true;

    static thread_local std::vector<uchar> outbuf;
    std::vector<int> params = {
            cv::IMWRITE_JPEG_QUALITY, m_iQuality
    };
    cv::imencode(".jpg", frame, outbuf, params);  //REMOVED FOR COMPATIBILITY
    // https://docs.opencv.org/3.4/d4/da8/group__imgcodecs.html#ga292d81be8d76901bff7988d18d2b42ac
    //std::cerr << "cv::imencode call disabled!" << std::endl;
    int outlen = static_cast<int>(outbuf.size());

    static thread_local char head[400];
    int iLen = sprintf(head, "--boundarydonotcross\r\nContent-Type: image/jpeg\r\nContent-Length: %zu\r\n\r\n", outlen);

    for (auto iter = mems.begin(); iter != mems.end(); ++iter) {
        if (!m_listener.GetServer()->Send(*iter, reinterpret_cast<const BYTE *>(head), iLen)
            || !m_listener.GetServer()->Send(*iter, outbuf.data(), outbuf.size())) {
            superType::delChannelMem(channel, *iter); //发送失败就移除
        }
    }

    return true;
}

MJPGSender::~MJPGSender() {

}

MJPGSender::MJPGSender(HttpServerWrap &listener) :
        m_iPort(-1),
        m_iTimeout(-1),
        m_iQuality(-1),
        m_listener(listener) {
}

bool MJPGSender::createPubChannel(const std::string &channel) {
    if (superType::createPubChannel(channel)) {
        m_listener.Register(HttpMethod::HTTP_METHOD_GET, channel, [=](HttpRequest *req, HttpResponse *resp) {
            resp->setHeader("Transfer-Encoding", "identity");
            resp->setHeader("Cache-Control",
                            "no-cache, no-store, must-revalidate, pre-check=0, post-check=0, max-age=0");
            resp->setHeader("Pragma", "no-cache");
            resp->setHeader("Connection", "close");
            resp->setHeader("Content-Type", "multipart/x-mixed-replace;boundary=boundarydonotcross");
            resp->setStatusCode(EnHttpStatusCode::HSC_OK);

            addChannelMem(channel, req->Connid());
        });
        return true;
    }
    return false;
}

bool MJPGSender::delChannel(const std::string &channel) {
    std::lock_guard<std::mutex> locker(m_mxtSubChannel);
    auto mems = superType::channelMems(channel);
    if (superType::delChannel(channel)) {
        if (m_listener.GetServer() && m_listener.GetServer()->HasStarted()) {
            for(auto elem : mems) {
                m_listener.GetServer()->Disconnect(elem);
            }
        }
    }
}

bool MJPGSender::addChannelMem(const std::string &channel, const CONNID &elem) {
    std::lock_guard<std::mutex> locker(m_mxtSubChannel);
    return ChannelManager::addChannelMem(channel, elem);
}

std::list<std::string> MJPGSender::channles() {
    std::lock_guard<std::mutex> locker(m_mxtSubChannel);
    return ChannelManager::channles();
}



////////////////////////////////////////////